<?php
/**
 * RBAC权限控制
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2016/12/10
 * Time: 21:25
 */

namespace app\common\controller;

use app\common\model\AppAccessLog;
use app\common\model\BoolStatus;
use app\common\model\Member;
use app\common\model\Access as AccessModel;
use app\common\model\RoleAccess as RoleAcessModel;
use app\common\model\UserRole;
use think\Controller;
use think\Cookie;
use think\Session;

class Base extends Controller
{

    /**
     * 登录的cookie
     * @var string
     */
    protected $authCookieName = 'Dadiba';

    /**
     * 登录的session
     * @var string
     */
    protected $authSessionName = 'uid';

    /**
     * 当前登录人信息
     * @var null
     */
    protected $currentUser = null;

    /**
     * 允许访问的控制器
     * @var array
     */
    protected $allowAllAction = [
        'login/index',
        'login/runlogin',
        'login/verify',
    ];

    /**
     * 无需认证的页面
     * @var array
     */
    public $ignoreUrl = [
        'error/forbidden',
        'login/index',
        'login/runlogin',
        'login/verify',
        'login/logout',
        'upload/uploadimg',
        'ueditor/index',
    ];

    /**
     * 保存去的权限链接
     * @var array
     */
    public $privilegeUrls = [];


    /**
     * 构造方法
     * AdminBase constructor.
     */
    public function __construct()
    {
        parent::__construct();
        $loginStatus       = $this->checkLoginStatus();
        $currentRequestUrl = strtolower($this->request->controller() . '/' . $this->request->action());
        if (!$loginStatus && !in_array($currentRequestUrl, $this->allowAllAction)) {
            if ($this->request->isAjax()) {
                $this->error('未登录,请返回用户中心');
            } else {
                $this->redirect(url('login/index'));//返回到登录页面
            }
            return false;
        }
        //保存所有的访问到数据库当中
        $postParams           = $this->request->post();
        $getParams            = $this->request->get();
        $AppAccessLogModel    = new AppAccessLog();
        $data                 = [];
        $data['uid']          = $this->currentUser ? $this->currentUser['id'] : 0;
        $data['target_url']   = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
        $data['query_params'] = json_encode(array_merge($postParams, $getParams));
        $data['ua']           = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
        $data['ip']           = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
        $data['created_time'] = date("Y-m-d H:i:s");
        $AppAccessLogModel->insert($data);

        /**
         * 判断权限的逻辑是
         * 取出当前登录用户的所属角色，
         * 在通过角色 取出 所属 权限关系
         * 在权限表中取出所有的权限链接
         * 判断当前访问的链接 是否在 所拥有的权限列表中
         */
        if (!$this->checkPrivilege($currentRequestUrl)) {
            return $this->redirect('error/forbidden');
        }

        return true;
    }

    /**
     * 检查是否有访问指定链接的权限
     * @param string $url 控制器/方法
     * @return bool
     */
    public function checkPrivilege($url)
    {
        //如果是超级管理员 也不需要权限判断
        if ($this->currentUser && $this->currentUser['is_admin']) {
            return true;
        }
        //有一些页面是不需要进行权限判断的
        if (in_array($url, $this->ignoreUrl)) {
            return true;
        }
        return in_array($url, $this->getRolePrivilege());
    }

    /**
     * 获取某用户的所有权限
     * @param int $uid 用户编号
     * @return array
     */
    public function getRolePrivilege($uid = 0)
    {
        if (!$uid && $this->currentUser) {
            $uid = $this->currentUser->id;
        }
        if (!$this->privilegeUrls) {
            //在通过角色 取出 所属 权限关系
            $userRoleModel = new UserRole();
            $roleIds       = $userRoleModel::hasWhere('role', ['status' => BoolStatus::OPEN])->where('uid', '=', $uid)->column('role_id');
            if ($roleIds) {

                $accessIds = RoleAcessModel::whereIn('role_id', $roleIds)->column('access_id');

                //在权限表中取出所有的权限链接
                $accessModel = new AccessModel();
                $list        = $accessModel->whereIn('id', $accessIds)->field('urls')->select();

                if ($list) {
                    foreach ($list as $_item) {
                        $tmpUrls   = [];
                        $tmpUrls[] = $_item->urls;
//                    $tmpUrls             = @json_decode($_item['urls'], true);    //暂时一个权限只保存一个规则
                        $this->privilegeUrls = array_merge($this->privilegeUrls, $tmpUrls);
                    }
                }
            }
        }

        return $this->privilegeUrls;
    }

    /**
     * 验证登录是否有效，返回 true or  false
     * @return bool
     */
    public function checkLoginStatus()
    {
        $authSession = Session::get($this->authSessionName);
        if (!$authSession) {
            return false;
        }
        $authToken = $uid = '';
        list($authToken, $uid) = explode("#", $authSession);
        if ($uid && preg_match("/^\d+$/", $uid)) {
            $userInfo = Member::get(['id' => $uid]);
            if (!$userInfo) {
                return false;
            }
            //校验码
            if ($authToken != $this->createAuthToken($userInfo['id'], $userInfo['name'], $userInfo['email'], $_SERVER['HTTP_USER_AGENT'])) {
                return false;
            }
            $this->currentUser = $userInfo;
            $this->assign('currentUser', $userInfo);
            return true;
        }
        return false;
    }

    /**
     * 登录态cookie
     * @param array $userInfo 用户基本信息
     */
    public function createLoginStatus($userInfo)
    {
        $authToken = $this->createAuthToken($userInfo['id'], $userInfo['name'], $userInfo['email'], $_SERVER['HTTP_USER_AGENT']);
        Cookie::set($this->authCookieName, $authToken . "#" . $userInfo['id']);
        Session::set($this->authSessionName, $authToken . "#" . $userInfo['id']);
    }

    /**
     * 用户相关信息生成加密校验码函数
     * @param int $uid 用户编号
     * @param string $name 用户名
     * @param string $email 邮箱
     * @param string $user_agent 浏览器
     * @return string
     */
    public function createAuthToken($uid, $name, $email, $user_agent)
    {
        return md5($uid . $name . $email . $user_agent);
    }
}